package org.osomit.sacct.account.registry.impl;

import org.osomit.sacct.account.registry.iface.AccountRegistryLoader;
import org.osomit.sacct.account.registry.iface.AccountRegistry;
import org.osomit.sacct.security.utils.SymmetricCipher;
import org.osomit.sacct.security.utils.Serializer;
import org.osomit.sacct.entity.ApplicationAccountRecord;
import org.osomit.sacct.entity.ApplicationAccountRecordSet;
import org.osomit.sacct.builder.ApplicationAccountRecordSetBuilder;
import org.osomit.sacct.server.config.iface.ServerConfig;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stringtree.json.JSONReader;

import java.io.*;
import java.util.List;

import com.google.inject.Inject;

/**
 * @author Jian Fang (John.Jian.Fang@gmail.com)
 *
 * Date: Jun 30, 2009
 */
public class FileAccountRegistryLoader implements AccountRegistryLoader {
	static int TYPICAL_SIZE = 1024;

    private ServerConfig config;

    private JSONReader reader = new JSONReader();

    private ApplicationAccountRecordSetBuilder builder = new ApplicationAccountRecordSetBuilder();

    private Log log = LogFactory.getLog(FileAccountRegistryLoader.class);

    public ServerConfig getConfig() {
        return config;
    }

    @Inject
    public void setConfig(ServerConfig config) {
        this.config = config;
    }

    protected String getAccountFileName(){
        return this.config.getAccountFileName();
    }

	public void load(AccountRegistry registry, String password){
        String fileName = this.getAccountFileName();
        if (fileName == null || password == null)
            return;

        try {
            FileInputStream fin = new FileInputStream(fileName);
            BufferedReader br = new BufferedReader(new InputStreamReader(fin));
            String line;
            StringBuffer sb = new StringBuffer(TYPICAL_SIZE);
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

            fin.close();
            br.close();

            if (sb.length() > 0) {
                byte[] encrypted = Base64.decodeBase64(sb.toString().getBytes());
//                byte[] decrypted = SymmetricCipher.desDecrypt(password, encrypted);
                byte[] decrypted = SymmetricCipher.aesDecrypt(password, encrypted);
                byte[] decompressed = Serializer.decompress(decrypted);
                
                Object object = reader.read(new String(decompressed));
                ApplicationAccountRecordSet set = builder.build(object);

                List<ApplicationAccountRecord> records = set.getRecords();

                if (records != null && records.size() > 0) {
                    for(ApplicationAccountRecord record: records){
                        registry.addAllAccounts(record.getClientId(), record.getAccounts());
                    }
                }
            }
        }catch (ClassNotFoundException e) {
           log.error("Cannot find class: " + e.getMessage());
            //TODO: need to re-throw Runtime Exception using exception hierarcy
            e.printStackTrace(); 
        } catch (FileNotFoundException e) {
            log.error("Cannot find file: " + fileName + e.getMessage());
            e.printStackTrace();
        }catch (IOException e) {
            log.error("IOException: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            log.error("Exception: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void persist(AccountRegistry registry, String password) {
        String fileName = this.getAccountFileName();
        if (fileName == null || registry == null || password == null)
            return;

        try {
            ApplicationAccountRecordSet set = registry.getAccountRecordSet();
            String json = set.toJSON().toString();
            byte[] serialized = Serializer.compress(json.getBytes());
//            byte[] encrypted = SymmetricCipher.desEncrypt(password, serialized);
            byte[] encrypted = SymmetricCipher.aesEncrypt(password, serialized);
            String result = new String(Base64.encodeBase64(encrypted), "UTF8");

            FileOutputStream fout = new FileOutputStream(fileName, false);
            fout.write(result.getBytes());
            fout.close();
        }catch (FileNotFoundException e) {
            log.error("Cannot find file: " + fileName + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            log.error("Exception: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
